/*
 * File Name：AudioRecorderMp3.java
 * Copyright：Copyright 2008-2014 CiWong.Inc. All Rights Reserved.
 * Description： AudioRecorderMp3.java
 * Modify By：jejun
 * Modify Date：2014-5-29
 * Modify Type：Add
 */
package com.ciwong.libs.audio.recorder;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.InvalidParameterException;

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;

import com.ciwong.libs.audio.jni.Mp3Encoder;

/**
 * Mp3音频录制
 * 
 * @author jejun
 * @version ciwong v.1.0 2014-5-29
 * @since ciwong v.1.0
 */
public class Mp3Recorder implements IAudioRecorder
{
    /**
     * 录音开始
     */
    public static final int MSG_REC_STARTED = 0;

    /**
     * 录音停止
     */
    public static final int MSG_REC_STOPPED = 1;

    /**
     * 录音失败：获取最小缓冲大小失败
     */
    public static final int MSG_ERROR_GET_MIN_BUFFERSIZE = 2;

    /**
     * 录音失败：创建文件失败
     */
    public static final int MSG_ERROR_CREATE_FILE = 3;

    /**
     * 录音已经开始
     */
    public static final int MSG_ERROR_REC_START = 4;

    /**
     * 
     */
    public static final int MSG_ERROR_AUDIO_RECORD = 5;

    /**
     * 
     */
    public static final int MSG_ERROR_AUDIO_ENCODE = 6;

    /**
     * 录音失败：写入文件失败
     */
    public static final int MSG_ERROR_WRITE_FILE = 7;

    /**
     * 录音失败：关闭文件失败
     */
    public static final int MSG_ERROR_CLOSE_FILE = 8;

    /**
     * 声音大小
     */
    public static final int MSG_DB = 9;

    private String mFilePath;

    private int mSampleRate;

    private boolean mIsRecording = false;

    private boolean mPuase = false;

    private Object async = new Object();

    private OnRecorderListener recorderListener;

    /**
     * 
     * @see Mp3Recorder#MSG_REC_STARTED
     * @see Mp3Recorder#MSG_REC_STOPPED
     * @see Mp3Recorder#MSG_ERROR_GET_MIN_BUFFERSIZE
     * @see Mp3Recorder#MSG_ERROR_CREATE_FILE
     * @see Mp3Recorder#MSG_ERROR_REC_START
     * @see Mp3Recorder#MSG_ERROR_AUDIO_RECORD
     * @see Mp3Recorder#MSG_ERROR_AUDIO_ENCODE
     * @see Mp3Recorder#MSG_ERROR_WRITE_FILE
     * @see Mp3Recorder#MSG_ERROR_CLOSE_FILE
     */
    private Handler mHandler = new Handler()
    {
        public void handleMessage(Message msg)
        {
            if (recorderListener != null)
            {
                recorderListener.onRecorderListener(msg.what);
            }
        };
    };

    /**
     * 构造函数
     * 
     * @param filePath
     *            存放的文件地址
     * 
     * @param sampleRate
     *            采样频率
     * 
     */
    public Mp3Recorder(String filePath, int sampleRate, Handler mHandler)
    {
        if (sampleRate <= 0)
        {
            throw new InvalidParameterException(
                    "Invalid sample rate specified.");
        }
        this.mFilePath = filePath;
        this.mSampleRate = sampleRate;
        this.mHandler = mHandler;
    }

    /**
     * 构造函数
     * 
     * @param filePath
     * 
     * @param sampleRate
     *            采样频率
     * 
     */
    public Mp3Recorder(String filePath, int sampleRate)
    {
        this(filePath, sampleRate, null);
    }

    @Override
    public void start()
    {
        if (mIsRecording)
        {
            return;
        }

        new Thread()
        {
            @Override
            public void run()
            {
                android.os.Process
                        .setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
                final int minBufferSize = AudioRecord.getMinBufferSize(
                        mSampleRate, AudioFormat.CHANNEL_IN_MONO,
                        AudioFormat.ENCODING_PCM_16BIT);
                if (minBufferSize < 0)
                {
                    if (mHandler != null)
                    {
                        mHandler.sendEmptyMessage(MSG_ERROR_GET_MIN_BUFFERSIZE);
                    }
                    return;
                }
                // getMinBufferSize
                // "W/AudioFlinger(75): RecordThread: buffer overflow"
                // AudioRecord audioRecord = new AudioRecord(
                // MediaRecorder.AudioSource.MIC, mSampleRate,
                // MediaRecorder.OutputFormat.THREE_GPP,
                // MediaRecorder.AudioEncoder.AMR_NB, minBufferSize * 2);
                AudioRecord audioRecord = new AudioRecord(
                        MediaRecorder.AudioSource.MIC, mSampleRate,
                        AudioFormat.CHANNEL_IN_MONO,
                        AudioFormat.ENCODING_PCM_16BIT, minBufferSize * 2);

                // PCM buffer size (5sec)
                short[] buffer = new short[mSampleRate * (16 / 8) * 1 * 5]; // SampleRate[Hz]
                                                                            // *
                                                                            // 16bit
                                                                            // *
                                                                            // Mono
                                                                            // *
                                                                            // 5sec
                byte[] mp3buffer = new byte[(int) (7200 + buffer.length * 2 * 1.25)];

                FileOutputStream output = null;
                try
                {
                    output = new FileOutputStream(new File(mFilePath));
                }
                catch (FileNotFoundException e)
                {
                    if (mHandler != null)
                    {
                        mHandler.sendEmptyMessage(MSG_ERROR_CREATE_FILE);
                    }
                    return;
                }

                // Lame init
                Mp3Encoder.init(mSampleRate, 1, mSampleRate, 32);

                mIsRecording = true; // 录音开始
                try
                {
                    try
                    {
                        audioRecord.startRecording();// 录音开始
                    }
                    catch (IllegalStateException e)
                    {
                        // 录音失败
                        if (mHandler != null)
                        {
                            mHandler.sendEmptyMessage(MSG_ERROR_REC_START);
                        }
                        return;
                    }

                    try
                    {
                        if (mHandler != null)
                        {
                            mHandler.sendEmptyMessage(MSG_REC_STARTED);
                        }

                        int readSize = 0;
                        int lastValue = 0;
                        while (mIsRecording)
                        {
                            readSize = audioRecord.read(buffer, 0,
                                    minBufferSize);
                            if (readSize < 0)
                            {
                                if (mHandler != null)
                                {
                                    mHandler.sendEmptyMessage(MSG_ERROR_AUDIO_RECORD);
                                }
                                break;
                            }
                            else if (readSize == 0)
	                        { 
                            	//录音权限没有放开
                            	if (mHandler != null)
	                            {
	                                mHandler.sendEmptyMessage(MSG_ERROR_AUDIO_RECORD);
	                            }
	                            break;
                            }
                            else
                            {
                                int v = 0;
                                // 将 buffer 内容取出，进行平方和运算
                                for (int i = 0; i < buffer.length; i++)
                                {
                                    // 这里没有做运算的优化，为了更加清晰的展示代码
                                    v += buffer[i] * buffer[i];
                                }
                                // 平方和除以数据总长度，得到音量大小。可以获取白噪声值，然后对实际采样进行标准化。
                                // 如果想利用这个数值进行操作，建议用 sendMessage 将其抛出，在
                                // Handler里进行处理。
                                // SDLog.e("spl---->", String.valueOf(v /
                                // (float) readSize));

                                // 计算了噪音,对音量进行调整：
                                // value 的 值 控制 为 0 到 100 之间 0为最小 》= 100为最大！！
                                int value = (int) (Math
                                        .abs((int) (v / (float) readSize) / 100) >> 1);
                                // SDLog.e("spl--value-->",
                                // String.valueOf(value));

                                // 计算分贝值：
                                // 那个值应该是声音的振幅，并不是音量的大小，
                                // 声音的大小应该是用分贝为单位的吧
                                double db = 10 * Math.log10(v
                                        / (double) readSize);
                                // SDLog.e("spl--db-->", String.valueOf(db));

                                if (lastValue != value && mHandler != null)
                                {
                                    Message message = mHandler
                                            .obtainMessage(MSG_DB);
                                    Bundle b = new Bundle();// 存放数据
                                    b.putString("v", String.valueOf(v
                                            / (float) readSize));
                                    b.putInt("value", value);
                                    b.putDouble("db", db);
                                    message.setData(b);
                                    mHandler.sendMessage(message);
                                }
                                int encResult = Mp3Encoder.encode(buffer,
                                        buffer, readSize, mp3buffer);
                                if (encResult < 0)
                                {
                                    if (mHandler != null)
                                    {
                                        mHandler.sendEmptyMessage(MSG_ERROR_AUDIO_ENCODE);
                                    }
                                    break;
                                }
                                if (encResult != 0)
                                {
                                    try
                                    {
                                        output.write(mp3buffer, 0, encResult);
                                    }
                                    catch (IOException e)
                                    {
                                        if (mHandler != null)
                                        {
                                            mHandler.sendEmptyMessage(MSG_ERROR_WRITE_FILE);
                                        }
                                        break;
                                    }
                                }
                            }
                            synchronized (async)
                            {
                                if (mPuase)
                                {
                                    try
                                    {
                                        async.wait();
                                    }
                                    catch (InterruptedException e)
                                    {
                                        e.printStackTrace();
                                    }
                                }
                            }
                        }

                        int flushResult = Mp3Encoder.flush(mp3buffer);
                        if (flushResult < 0)
                        {
                            if (mHandler != null)
                            {
                                mHandler.sendEmptyMessage(MSG_ERROR_AUDIO_ENCODE);
                            }
                        }
                        if (flushResult != 0)
                        {
                            try
                            {
                                output.write(mp3buffer, 0, flushResult);
                            }
                            catch (IOException e)
                            {
                                if (mHandler != null)
                                {
                                    mHandler.sendEmptyMessage(MSG_ERROR_WRITE_FILE);
                                }
                            }
                        }

                        try
                        {
                            output.close();
                        }
                        catch (IOException e)
                        {
                            if (mHandler != null)
                            {
                                mHandler.sendEmptyMessage(MSG_ERROR_CLOSE_FILE);
                            }
                        }
                    }
                    finally
                    {
                        try
                        {
                            audioRecord.stop();
                            audioRecord.release();
                        }
                        catch (IllegalStateException e)
                        {
                            e.printStackTrace();
                        }
                    }
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
                finally
                {
                    Mp3Encoder.close();
                    mIsRecording = false;
                }

                if (mHandler != null)
                {
                    mHandler.sendEmptyMessage(MSG_REC_STOPPED);
                }
            }
        }.start();
    }

    @Override
    public void puase()
    {
        synchronized (async)
        {
            mPuase = true;
        }
    }

    @Override
    public void resume()
    {
        synchronized (async)
        {
            if (mPuase)
            {
                mPuase = false;
                async.notify();
            }
        }
    }

    @Override
    public void stop()
    {
        resume();
        mIsRecording = false;
    }

    @Override
    public boolean isRecording()
    {
        return mIsRecording;
    }

    /**
     * 设置消息处理器
     * 
     * @param handler
     *            {@link Handler}
     * 
     * @see Mp3Recorder#MSG_REC_STARTED
     * @see Mp3Recorder#MSG_REC_STOPPED
     * @see Mp3Recorder#MSG_ERROR_GET_MIN_BUFFERSIZE
     * @see Mp3Recorder#MSG_ERROR_CREATE_FILE
     * @see Mp3Recorder#MSG_ERROR_REC_START
     * @see Mp3Recorder#MSG_ERROR_AUDIO_RECORD
     * @see Mp3Recorder#MSG_ERROR_AUDIO_ENCODE
     * @see Mp3Recorder#MSG_ERROR_WRITE_FILE
     * @see Mp3Recorder#MSG_ERROR_CLOSE_FILE
     */
    public void setHandler(Handler handler)
    {
        this.mHandler = handler;
    }

    /**
     * {@inheritDoc}<br />
     * 如果设置了{@link #setHandler(Handler)}或在构造函数中传入了{@link Handler}此监听无效
     * {@link OnRecorderListener#onRecorderListener(int status)} ,其中status值如下:<br />
     * 
     * {@link Mp3Recorder#MSG_REC_STARTED} {@link Mp3Recorder#MSG_REC_STOPPED}<br />
     * {@link Mp3Recorder#MSG_ERROR_GET_MIN_BUFFERSIZE}<br />
     * {@link Mp3Recorder#MSG_ERROR_CREATE_FILE}<br />
     * {@link Mp3Recorder#MSG_ERROR_REC_START}<br />
     * {@link Mp3Recorder#MSG_ERROR_AUDIO_RECORD}<br />
     * {@link Mp3Recorder#MSG_ERROR_AUDIO_ENCODE}<br />
     * {@link Mp3Recorder#MSG_ERROR_WRITE_FILE}<br />
     * {@link Mp3Recorder#MSG_ERROR_CLOSE_FILE}
     */
    @Override
    public void setOnRecorderListener(OnRecorderListener listener)
    {
        recorderListener = listener;
    }

}
